package org.nehc.algorithm.redissonutils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * Redisson 分布式锁操作工具类
 * 提供分布式锁的获取、释放等操作
 *
 * @author system
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RedissonLockUtils {

    private static final RedissonClient CLIENT = RedissonUtils.getClient();

    /**
     * 尝试获取分布式锁
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @param waitTime  等待时间
     * @param leaseTime 锁的持有时间
     * @return true=获取锁成功；false=获取锁失败
     */
    public static Boolean tryLock(final String cacheName, final String key,
                                  Duration waitTime, Duration leaseTime) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        try {
            return rLock.tryLock(waitTime.toMillis(), leaseTime.toMillis(), TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("获取分布式锁异常，key: {}", key, e);
            Thread.currentThread().interrupt();
            return false;
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                log.info("获取分布式锁成功，key: {}", key);
            } else {
                log.warn("获取分布式锁失败，key: {}", key);
            }
        }
    }

    /**
     * 尝试获取分布式锁（带超时时间）
     *
     * @param cacheName    缓存名称
     * @param key          锁的键值
     * @param timeoutMills 超时时间（毫秒）
     * @return true=获取锁成功；false=获取锁失败
     */
    public static Boolean tryLock(final String cacheName, final String key, Integer timeoutMills) {
        Duration timeout = Duration.ofMillis(timeoutMills);
        return tryLock(cacheName, key, timeout, timeout);
    }

    /**
     * 尝试获取分布式锁（默认超时时间）
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return true=获取锁成功；false=获取锁失败
     */
    public static Boolean tryLock(final String cacheName, final String key) {
        return tryLock(cacheName, key, 3000);
    }

    /**
     * 获取可重入锁
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return 可重入锁对象
     */
    public static RLock getReentrantLock(final String cacheName, final String key) {
        return CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
    }

    /**
     * 释放分布式锁
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     */
    public static void unLock(final String cacheName, final String key) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        try {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
                log.info("释放分布式锁成功，key: {}", key);
            } else {
                log.warn("当前线程未持有锁，无法释放，key: {}", key);
            }
        } catch (Exception e) {
            log.error("释放分布式锁异常，key: {}", key, e);
            throw e;
        }
    }

    /**
     * 释放锁对象
     *
     * @param rLock 锁对象
     */
    public static void unLock(RLock rLock) {
        if (rLock != null) {
            try {
                if (rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                    log.info("释放分布式锁成功");
                } else {
                    log.warn("当前线程未持有锁，无法释放");
                }
            } catch (Exception e) {
                log.error("释放分布式锁异常", e);
                throw e;
            }
        }
    }

    /**
     * 检查锁是否被当前线程持有
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return true=当前线程持有锁；false=当前线程未持有锁
     */
    public static boolean isHeldByCurrentThread(final String cacheName, final String key) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        return rLock.isHeldByCurrentThread();
    }

    /**
     * 检查锁是否被锁定
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return true=锁被锁定；false=锁未被锁定
     */
    public static boolean isLocked(final String cacheName, final String key) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        return rLock.isLocked();
    }

    /**
     * 获取锁的剩余持有时间
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return 剩余持有时间（毫秒），-1表示永久持有，-2表示锁不存在
     */
    public static long remainTimeToLive(final String cacheName, final String key) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        return rLock.remainTimeToLive();
    }

    /**
     * 强制释放锁（谨慎使用）
     *
     * @param cacheName 缓存名称
     * @param key       锁的键值
     * @return true=释放成功；false=释放失败
     */
    public static boolean forceUnlock(final String cacheName, final String key) {
        RLock rLock = CLIENT.getLock(RedissonUtils.getKey(cacheName, key));
        try {
            boolean result = rLock.forceUnlock();
            if (result) {
                log.warn("强制释放分布式锁成功，key: {}", key);
            } else {
                log.warn("强制释放分布式锁失败，锁可能不存在，key: {}", key);
            }
            return result;
        } catch (Exception e) {
            log.error("强制释放分布式锁异常，key: {}", key, e);
            return false;
        }
    }
}